package Component

import (
	"fmt"
	"github.com/pkg/sftp"
	"golang.org/x/crypto/ssh"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path"
	"time"
)

type UploadFolderComponent struct {
	remoteServer string
	remoteDir    string
	localDir     string

	userName string
	password string
}

func (self *UploadFolderComponent) Run() (err error) {
	sshConfig := &ssh.ClientConfig{
		User: self.userName,
		Auth: []ssh.AuthMethod{
			ssh.Password(self.password),
		},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
		ClientVersion:   "",
		Timeout:         10 * time.Second,
	}

	sshClient, err := ssh.Dial("tcp", self.remoteServer, sshConfig)
	if err != nil {
		return
	}
	defer sshClient.Close()

	sftpClient, err := sftp.NewClient(sshClient)
	if err != nil {
		return
	}

	defer sftpClient.Close()

	return uploadDir(sftpClient, self.localDir, self.remoteDir)
}

func clearDir(sftpClient *sftp.Client, remoteDir string) (err error) {
	files, err := sftpClient.ReadDir(remoteDir)
	if err != nil {
		return
	}

	for _, file := range files {
		filePath := path.Join(remoteDir, file.Name())

		fmt.Printf("Delete %s\r\n", filePath)

		if file.IsDir() {
			err = deleteDir(sftpClient, filePath)
		} else {
			err = sftpClient.Remove(filePath)
		}

		if err != nil {
			return
		}
	}

	return
}

func deleteDir(sftpClient *sftp.Client, remoteDir string) (err error) {
	clearDir(sftpClient, remoteDir)
	return sftpClient.RemoveDirectory(remoteDir)
}

func any(files []os.FileInfo, fc func(info os.FileInfo) bool) bool {
	for _, f := range files {
		if fc(f) {
			return true
		}
	}

	return false
}

func uploadDir(sftpClient *sftp.Client, localDir string, remoteDir string) (err error) {
	fmt.Printf("%s=>%s\r\n", localDir, remoteDir)

	_, err = sftpClient.Stat(remoteDir)
	if err != nil {
		sftpClient.Mkdir(remoteDir)
	}

	//clearDir(sftpClient, remoteDir)

	files, err := sftpClient.ReadDir(remoteDir)
	if err != nil {
		return
	}

	localFiles, err := ioutil.ReadDir(localDir)
	if err != nil {
		return
	}

	for _, rFile := range files {
		if any(localFiles, func(info os.FileInfo) bool {
			return info.Name() == rFile.Name()
		}) != true {
			delPath := path.Join(remoteDir, rFile.Name())
			fmt.Printf("Delete %s\r\n", delPath)

			if rFile.IsDir() {
				deleteDir(sftpClient, delPath)
			} else {
				sftpClient.Remove(delPath)
			}
		}
	}

	for _, lFile := range localFiles {
		fileName := lFile.Name()

		localPath := path.Join(localDir, fileName)
		remotePath := path.Join(remoteDir, fileName)

		if lFile.IsDir() {
			err = uploadDir(sftpClient, localPath, remotePath)
			if err != nil {
				return
			}
		} else {
			uploadFile(sftpClient, localPath, remotePath)
		}
	}

	return
}

func uploadFile(client *sftp.Client, localPath string, remotePath string) {
	remoteFile, err := client.Create(remotePath)
	if err != nil {
		log.Fatalln(err.Error())
	}
	defer remoteFile.Close()

	fmt.Printf("%s=>%s\r\n", localPath, remotePath)

	//打开本地文件file.dat
	localFile, err := os.Open(localPath)
	if err != nil {
		return
	}
	defer localFile.Close()

	//本地文件流拷贝到上传文件流
	_, err = io.Copy(remoteFile, localFile)
	if err != nil {
		return
	}
}

func (self *UploadFolderComponent) SetParameter(name string, parameter string) {
	if name == UserName {
		self.userName = parameter
	} else if name == PassWord {
		self.password = parameter
	} else if name == RmoteServer {
		self.remoteServer = parameter
	} else if name == RmoteDir {
		self.remoteDir = parameter
	} else if name == LocalDir {
		self.localDir = parameter
	}
}
